﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Objects;
using System.Linq;
using Abs.Core.Models.Booking;

namespace Abs.Core.DAL.EntityFramework
{
    internal class AppointmentRepository : EntityRepository<Appointment>
    {
        public override bool Create(Appointment item)
        {
            this.AbsContext.Appointments.Add(item);
            var isSuccess = this.AbsContext.SaveChanges();

            if(isSuccess > AppointmentRepository.NoChanges)
                return true;
            return false;
        }//end of public override bool Create(Appointment item)

        public override IList<Appointment> SelectAll()
        {
            var appointments = this.AbsContext.Appointments
                                    .Include("User")
                                    .Include("Room")
                                    .Select(appointment => appointment);
            return appointments.ToList();
        }//end of public override IList<Appointment> SelectAll()

        /// <summary>
        ///     Select an appointment based on the given AppointmentId
        ///     as a primary key.
        /// </summary>
        /// <param name="id">
        ///     id[0] represents AppointmentId.
        /// </param>
        /// <returns>
        ///     Return an appointment object if the appoinment is founded. 
        ///     Otherwise, return null.
        /// </returns>
        public override Appointment Select(object[] id)
        {
            var appointmentId = Convert.ToInt32(id[0]);
            var appointment = this.AbsContext.Appointments
                                  .Select(booking => booking)
                                  .Include("Room")
                                  .Include("User")
                                  .FirstOrDefault(booking => booking.AppointmentId == appointmentId);

            return appointment;
        }//end of public override Appointment Select(object[] id)

        public override bool Update(Appointment item)
        {
            this.AbsContext.Entry(item).State = EntityState.Modified;
            var isSuccess = this.AbsContext.SaveChanges();

            if (isSuccess > AppointmentRepository.NoChanges)
                return true;
            return false;
        }//end of public override bool Update(Appointment item)

        /// <summary>
        ///     Delete an appointment based on the given AppointmentId
        ///     as primary key.
        /// </summary>
        /// <param name="id">
        ///     id[0] represenets AppointmentId.
        /// </param>
        /// <returns>
        ///     Return true, if the appointment successfully deleted.
        ///     Otherwise, return false.
        /// </returns>
        public override bool Delete(object[] id)
        {
            var appointmentId = Convert.ToInt32(id[0]);
            var appointment = this.AbsContext.Appointments.Find(appointmentId);
            this.AbsContext.Appointments.Remove(appointment);
            var isSuccess = this.AbsContext.SaveChanges();

            if(isSuccess > AppointmentRepository.NoChanges)
                return true;
            return false;
        }//end of public override bool Delete(object[] id)

        internal IList<Appointment> GetAppointmentOnDate(DateTime date)
        {
            var appointments = this.AbsContext.Appointments.Select(appointment => appointment)
                                   .Include("Room")
                                   .Include("User")
                                   .Where(appointment => EntityFunctions.TruncateTime(date) ==
                                                         EntityFunctions.TruncateTime(appointment.StartTime));
            return appointments.ToList();
        }//end of internal IList<Appointment> GetAppointmentOnDate(DateTime date)

        /// <summary>
        /// Check if anyone has booked the room at a particular time.
        /// This function is used before adding a new appointment or editing 
        /// an existing appointment. If someone has booked the room, no one 
        /// is able to book a new appointment or edit an existing appointment.
        /// 
        /// Based on the MSDN documentation:
        /// 
        ///         public int CompareTo(DateTime value)
        /// 
        ///    Less than zero - This instance is earlier than value
        ///              Zero - This instance is the same as value
        /// Greater than zero - This instance is later than value
        /// </summary>
        /// <param name="roomId">
        ///     The room to be booked.
        /// </param>
        /// <param name="startTime">
        ///     The start date and time of the room to be booked.
        /// </param>
        /// <param name="endTime">
        ///     The end date and time of the room to be booked.
        /// </param>
        /// <param name="appointmentId">
        ///     Used default value to check anyone has booked or not, 
        ///     before adding a new appointment.
        ///     Otherwise, provide the appointmentId before 
        ///     editing an exisiting appointment.
        /// </param>
        /// <returns>
        ///     Returns true if anyone has booked the room at particular
        ///     time. Otherwise, return false.
        /// </returns>
        internal bool IsBookingOverlapped(int roomId, DateTime startTime,
                                     DateTime endTime, int appointmentId = 0)
        {
            var appointments = from appointment in this.AbsContext.Appointments
                               where appointment.RoomId == roomId &&
                                     appointment.AppointmentId != appointmentId &&
                                     ((appointment.StartTime.CompareTo(startTime) <= 0 &&
                                       appointment.EndTime.CompareTo(startTime) >= 0) ||
                                      (appointment.StartTime.CompareTo(endTime) <= 0 &&
                                       appointment.EndTime.CompareTo(endTime) >= 0) ||
                                      (startTime.CompareTo(appointment.StartTime) <= 0 &&
                                       endTime.CompareTo(appointment.EndTime) >= 0))
                               select appointment;
            
            if (appointments.FirstOrDefault() != null)
                return true;
            return false;
        }//end of private bool IsBookingOverlapped
    }//end of internal class AppointmentRepository : EntityRepository<Appointment>
}//end of namespace ABS.DAL.EntityFramework